winsafe\kernel\utilities/file.rs
1use crate::co;
2use crate::decl::*;
3use crate::guard::*;
4
5/// Access types for [`File::open`](crate::File::open) and
6/// [`FileMapped::open`](crate::FileMapped::open).
7#[derive(Clone, Copy, PartialEq, Eq, Hash)]
8pub enum FileAccess {
9 /// Opens the file as read-only. Fails if the file doesn't exist.
10 ExistingReadOnly,
11 /// Opens the file as read/write. Fails if the file doesn't exist.
12 ExistingRW,
13 /// Opens the file as read/write. If the file doesn't exist, it will be
14 /// created.
15 OpenOrCreateRW,
16 /// Creates a new file as read/write. Fails if the file already exists.
17 CreateRW,
18}
19
20impl std::fmt::Display for FileAccess {
21 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
22 write!(
23 f,
24 "{}",
25 match self {
26 FileAccess::ExistingReadOnly => "Existing file, read-only",
27 FileAccess::ExistingRW => "Existing file, read and write",
28 FileAccess::OpenOrCreateRW =>
29 "Open existing file or create new file, read and write",
30 FileAccess::CreateRW => "Create new file, read and write",
31 }
32 )
33 }
34}
35
36/// Manages an [`HFILE`](crate::HFILE) handle, which provides file read/write
37/// and other operations. It is closed automatically when the object goes out of
38/// scope.
39///
40/// This is an alternative to the standard [`std::fs::File`], with a possibly
41/// faster implementation since it's Windows-only.
42///
43/// If you just want to read the file, consider memory-mapping it with
44/// [`FileMapped`](crate::FileMapped), which tends to be faster.
45///
46/// # Examples
47///
48/// Reading the contents as a string:
49///
50/// ```no_run
51/// use winsafe::{self as w, prelude::*};
52///
53/// let f = w::File::open(
54/// "C:\\Temp\\foo.txt",
55/// w::FileAccess::ExistingRW,
56/// )?;
57/// let raw_bytes = f.read_all()?;
58/// let text = w::WString::parse(&raw_bytes)?.to_string();
59/// # w::SysResult::Ok(())
60/// ```
61///
62/// Erasing the file and writing a string:
63///
64/// ```no_run
65/// use winsafe::{self as w, prelude::*};
66///
67/// let f = w::File::open(
68/// "C:\\Temp\\foo.txt",
69/// w::FileAccess::OpenOrCreateRW,
70/// )?;
71/// f.set_size(0)?; // truncate
72/// f.write("My text".as_bytes())?;
73/// # w::SysResult::Ok(())
74/// ```
75pub struct File {
76 hfile: CloseHandleGuard<HFILE>,
77}
78
79impl File {
80 /// Opens a file with the desired access.
81 #[must_use]
82 pub fn open(file_path: &str, access: FileAccess) -> SysResult<Self> {
83 let (acc, share, disp) = match access {
84 FileAccess::ExistingReadOnly => {
85 (co::GENERIC::READ, Some(co::FILE_SHARE::READ), co::DISPOSITION::OPEN_EXISTING)
86 },
87 FileAccess::ExistingRW => {
88 (co::GENERIC::READ | co::GENERIC::WRITE, None, co::DISPOSITION::OPEN_EXISTING)
89 },
90 FileAccess::OpenOrCreateRW => {
91 (co::GENERIC::READ | co::GENERIC::WRITE, None, co::DISPOSITION::OPEN_ALWAYS)
92 },
93 FileAccess::CreateRW => {
94 (co::GENERIC::READ | co::GENERIC::WRITE, None, co::DISPOSITION::CREATE_NEW)
95 },
96 };
97
98 let (hfile, _) = HFILE::CreateFile(
99 file_path,
100 acc,
101 share,
102 None,
103 disp,
104 co::FILE_ATTRIBUTE::NORMAL,
105 None,
106 None,
107 None,
108 )?;
109 Ok(Self { hfile })
110 }
111
112 /// Truncates the file size to zero, then writes the bytes.
113 pub fn erase_and_write(&self, data: &[u8]) -> SysResult<()> {
114 self.set_size(0)?;
115 self.write(data)
116 }
117
118 /// Returns the underlying file handle.
119 #[must_use]
120 pub fn hfile(&self) -> &HFILE {
121 &*self.hfile
122 }
123
124 /// Returns the position of the file pointer by calling
125 /// [`HFILE::SetFilePointerEx`](crate::HFILE::SetFilePointerEx).
126 #[must_use]
127 pub fn pointer_offset(&self) -> SysResult<u64> {
128 self.hfile
129 .SetFilePointerEx(0, co::FILE_STARTING_POINT::CURRENT) // https://stackoverflow.com/a/17707021/6923555
130 .map(|off| off as _)
131 }
132
133 /// Returns the size of the file by calling
134 /// [`HFILE::GetFileSizeEx`](crate::HFILE::GetFileSizeEx), allocates the
135 /// `Vec` buffer, then reads all the file bytes by calling
136 /// [`HFILE::ReadFile`](crate::HFILE::ReadFile).
137 ///
138 /// Note that the API limits the reading up to 4 GB.
139 #[must_use]
140 pub fn read_all(&self) -> SysResult<Vec<u8>> {
141 self.set_pointer_offset(0)?;
142 let mut buf = vec![0x00; self.size()? as _];
143 self.read_buffer(&mut buf)?;
144 Ok(buf)
145 }
146
147 /// Calls [`HFILE::ReadFile`](crate::HFILE::ReadFile) to read at most
148 /// `buffer.len()` bytes from the file, starting at the current file pointer
149 /// offset. Returns how many bytes were actually read. The file pointer is
150 /// then incremented by the number of bytes read.
151 ///
152 /// Note that the API limits the reading up to 4 GB.
153 pub fn read_buffer(&self, buffer: &mut [u8]) -> SysResult<u32> {
154 self.hfile.ReadFile(buffer)
155 }
156
157 /// Sets the position of the file pointer by calling
158 /// [`HFILE::SetFilePointerEx`](crate::HFILE::SetFilePointerEx).
159 pub fn set_pointer_offset(&self, offset: u64) -> SysResult<()> {
160 self.hfile
161 .SetFilePointerEx(offset as _, co::FILE_STARTING_POINT::BEGIN)
162 .map(|_| ())
163 }
164
165 /// Truncates or expands the file by calling
166 /// [`HFILE::SetFilePointerEx`](crate::HFILE::SetFilePointerEx) and
167 /// [`HFILE::SetEndOfFile`](crate::HFILE::SetEndOfFile), then sets the file
168 /// pointer to the beginning of the file.
169 ///
170 /// If the size is increased, the contents in the new area are undefined.
171 ///
172 /// Note that sometimes the system will make the file larger than you
173 /// specified, so don't bindly trust this method.
174 pub fn set_size(&self, num_bytes: u64) -> SysResult<()> {
175 self.set_pointer_offset(num_bytes)?;
176 self.hfile.SetEndOfFile()?;
177 self.set_pointer_offset(0)
178 }
179
180 /// Returns the size of the file by calling
181 /// [`HFILE::GetFileSizeEx`](crate::HFILE::GetFileSizeEx).
182 #[must_use]
183 pub fn size(&self) -> SysResult<u64> {
184 self.hfile.GetFileSizeEx()
185 }
186
187 /// Returns, in current time zone, 3 times of the file, respectively:
188 /// 1. creation time;
189 /// 2. last access time;
190 /// 3. last write time.
191 #[must_use]
192 pub fn times(&self) -> SysResult<(SYSTEMTIME, SYSTEMTIME, SYSTEMTIME)> {
193 let (ft_creation, ft_last_access, ft_last_write) = self.hfile.GetFileTime()?;
194
195 let st_creation_utc = FileTimeToSystemTime(&ft_creation)?;
196 let st_last_access_utc = FileTimeToSystemTime(&ft_last_access)?;
197 let st_last_write_utc = FileTimeToSystemTime(&ft_last_write)?;
198
199 let st_creation_local = SystemTimeToTzSpecificLocalTime(None, &st_creation_utc)?;
200 let st_last_access_local = SystemTimeToTzSpecificLocalTime(None, &st_last_access_utc)?;
201 let st_last_write_local = SystemTimeToTzSpecificLocalTime(None, &st_last_write_utc)?;
202
203 Ok((st_creation_local, st_last_access_local, st_last_write_local))
204 }
205
206 /// Writes the bytes at the current file pointer by calling
207 /// [`HFILE::WriteFile`](crate::HFILE::WriteFile).
208 ///
209 /// This method will fail if the file was opened with
210 /// [`FileAccess::ExistingReadOnly`](crate::FileAccess::ExistingReadOnly).
211 pub fn write(&self, data: &[u8]) -> SysResult<()> {
212 self.hfile.WriteFile(data).map(|_| ())
213 }
214}